package com.ap.core.dao;

import com.ap.core.domain.Identifiable;
import org.apache.commons.io.IOUtils;
import org.hibernate.Query;
import org.hibernate.SQLQuery;

import javax.persistence.NoResultException;
import javax.persistence.PersistenceException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.sql.Blob;
import java.sql.SQLException;


/**
 * User: Ranjit.Singh
 * Abstract class providing utility methods for reading from and writing to BLOB fields.
 * 
 * @param <T>
 *            generic type for the persistent entity
 * @param <U>
 *            generic type for the type of primary key / id
 */
public abstract class AbstractBlobJpaDao<T extends Identifiable<U>, U> extends AbstractAPCoreBaseJpaDao<T, U> {

    /**
     * Write an input stream into a BLOB field.
     * 
     * @param id
     *            unique identifier for the row to which BLOB data will be written
     * @param inputStream
     *            the input stream that points to data that needs to be written to the store
     */
    protected void writeBlobOutput(final Long id, final InputStream inputStream) {
        OutputStream outputStream;
        final Query nativeQuery = getSession().createSQLQuery(getBlobWriteSql());
        nativeQuery.setParameter(0, id);
        
        try {
            final Blob blob = (Blob) nativeQuery.uniqueResult();
            outputStream = blob.setBinaryStream(0);
        } catch (final NoResultException nre) {
            throw new PersistenceException(nre);
        } catch (final SQLException se) {
            throw new PersistenceException(se);
        }

        // copy the data
        try {
            IOUtils.copy(inputStream, outputStream);
            outputStream.flush();
        } catch (final IOException e) {
            throw new PersistenceException(e);
        } finally {
            IOUtils.closeQuietly(inputStream);
            IOUtils.closeQuietly(outputStream);
        }

    }

    /**
     * Return an input stream for reading data from a BLOB field.
     * 
     * @param id
     *            unique identifier for the row from which BLOB data will be read
     * @return an input stream from which data can be read
     */
    protected InputStream getBlobInputStream(final Long id) {
        try {
            final SQLQuery nativeQuery = getSession().createSQLQuery(getBlobReadSql());
            nativeQuery.setParameter(0, id);
            final Blob outputBlob = (Blob) nativeQuery.uniqueResult();
            return outputBlob.getBinaryStream();
        } catch (final NoResultException nre) {
            throw new PersistenceException(nre);
        } catch (final SQLException se) {
            throw new PersistenceException(se);
        }
    }

    /**
     * Method to be implemented by subclasses to define the SQL query to be used to write to a BLOB column.
     * 
     * @return BLOB write SQL query
     */
    protected abstract String getBlobWriteSql();

    /**
     * Method to be implemented by subclasses to define the SQL query to be used to read from a BLOB column.
     * 
     * @return BLOB read SQL query
     */
    protected abstract String getBlobReadSql();

}